/**
 * 
 */
package org.vsg.stock.core.indicator.kline;

import java.text.DecimalFormat;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

/**
 * @author Bill Vison
 *
 */
class DegreesCoordinatePointClusterer {

	private List<DegreesCoordinatePoint> points;
	
	private double maxNeighborInstance = 0.6;
	
	public void setDegreesCoordinatePoints(List<DegreesCoordinatePoint> points ) {
		this.points = points;
	}
	
	public double getMaxNeighborInstance() {
		return maxNeighborInstance;
	}

	public void setMaxNeighborInstance(double maxNeighborInstance) {
		this.maxNeighborInstance = maxNeighborInstance;
	}




	public List<Cluster<DegreesCoordinatePoint>> buildClusterer() {
	
		
		List<Cluster<DegreesCoordinatePoint>> all = createAllClusterer();
		
		
		Cluster<DegreesCoordinatePoint> comparedCluster = null;
		for ( Iterator<Cluster<DegreesCoordinatePoint>> outerIter = all.iterator() ; outerIter.hasNext(); ) {
			
			comparedCluster = outerIter.next();
			
			if ( comparedCluster.isEmpty() ) {
				outerIter.remove();
				continue;
			}
		

			
			for (Iterator<Cluster<DegreesCoordinatePoint>> clusIter = all.iterator() ; clusIter.hasNext() ; ) {
				Cluster inner = clusIter.next();
				
				if ( inner.equals(comparedCluster)) {
					continue;
				}
				
				double keyInstance = Math.abs( inner.getCenterKeyValue() - comparedCluster.getCenterKeyValue() );
				
				double pointInstance = Math.abs( inner.getCenterPointValue() - comparedCluster.getCenterPointValue() );
				
				double comparedValue = keyInstance > pointInstance ? keyInstance : pointInstance;
				


				if ( comparedValue < maxNeighborInstance) {
					for ( int j = 0 ; j < inner.size() ; j++ ) {
						comparedCluster.joinIn( (DegreesCoordinatePoint)inner.remove(j) );
					}
				}				
			}

		}

 
		
		return all;
	}
	
	
	private List<Cluster<DegreesCoordinatePoint>> createAllClusterer() {
		
		List<Cluster<DegreesCoordinatePoint>> result = new Vector<Cluster<DegreesCoordinatePoint>>();
		
		for (int i = 0 ; i < points.size() ; i++) {
			Cluster<DegreesCoordinatePoint> clus = new Cluster<DegreesCoordinatePoint>();
			clus.add( points.get(i) );
			result.add( clus );
		}
		
		return result;
	}
	
	
	
	
	class Cluster<T extends DegreesCoordinatePoint> extends Vector{
		
		private DecimalFormat nf = new DecimalFormat("##0.0000");			
		
		public void joinIn(DegreesCoordinatePoint point ) {
			
			if ( this.contains( point )) {
				return ;
			}
			
			this.add( point );
			
			// --- sort the value ----
			Collections.sort(this , new DegreesCPComparator() );
			
		}
		
		
		
		public double getCenterKeyValue() {
			
			// --- list key value ---
			if (this.size() == 1) {
				DegreesCoordinatePoint point = (DegreesCoordinatePoint)this.firstElement();
				return point.getDegrees();
			}
			else if (this.size() == 0) {
				return 0;
			}
			
			double subTotal = 0;
			for (int i = 0 ; i < this.size() ; i++) {
				DegreesCoordinatePoint point = (DegreesCoordinatePoint)this.firstElement();
				subTotal += point.getDegrees();
			}
			
			double avg = subTotal / this.size();
			
			String angleDegressValue = nf.format(avg);
			
			double result =  Double.parseDouble( angleDegressValue  ); ;

			return result; 
		}
		
		public double getCenterPointValue() {
			
			// --- list key value ---
			if (this.size() == 1) {
				DegreesCoordinatePoint point = (DegreesCoordinatePoint)this.firstElement();
				return point.getDegrees();
			} else if (this.size() == 0) {
				return 0;
			}
			
			int half = this.size() / 2;
			int mod = this.size() % 2;
			
			double avg = 0;
			if ( mod == 0 ) {
				// --- half value ---
				DegreesCoordinatePoint point1 = (DegreesCoordinatePoint)this.get( half - 1 );
				DegreesCoordinatePoint point2 = (DegreesCoordinatePoint)this.get( half );
				avg = ( point1.getDegrees() + point2.getDegrees() ) / 2;
			} else {
				DegreesCoordinatePoint point1 = (DegreesCoordinatePoint)this.get( half );
				 avg = point1.getDegrees();
			}
			
			
			return avg;
		}
		
		
	}
	
	
	
		
	
}
